#include <stdint.h>
#include <stdio.h>
#include <memory.h>
#include <shell.h>
#include <text_video.h>

volatile uint8_t x, y;

void setup_text_video(void)
{
    ui_monitor = false;
    cls();
}

static void scroll(void)
{
    uint8_t key;

    if(y < 24){return;}
    
    if(ui_monitor == true)
    {
        while(1)
        {
            key = get_key();
            if(key == '\n'){ui_monitor = false; break;}
            else if(key == ' '){break;}
        }
    }
    
    copy_memory((uint8_t*)TEXT_VIDEO_RAM_START, (uint8_t*)(TEXT_VIDEO_RAM_START + (80 * 2)), (23 * (80 * 2)));
    clear_memory((uint8_t*)(TEXT_VIDEO_RAM_START + (23 * (80 * 2))), (80 * 2));
    
    y = 23;
}

void cls(void)
{
    set_memory_16((uint8_t*)TEXT_VIDEO_RAM_START, (uint16_t)(0x0000 | '_'), TEXT_VIDEO_RAM_SIZE);
    x = y = 0x00;
}

void start_ui_more_monitor(const char * msg)
{   
    info_bar_printf(msg);
    ui_monitor = true;
}

static void print_char(const char ch)
{
    if(ch == '\n')
    {y++; x = 0; scroll(); return;}
    
    if(ch == '\b')
    {
        if(x){x--;}
        else{y--; x = 80-1;}
        return;
    }
    
    *(uint16_t*)(TEXT_VIDEO_RAM_START + ((y * (80 * 2)) + (x++ * 2))) = (uint16_t)(0x0700 | ch);

    if(x >= 80){y++; x = 0; scroll(); return;}
}

void print_string(const char * const str)
{
    uint32_t i;
    
    i = 0;
    
    while(1)
    {
        if(*(uint8_t*)(str + i))
        {
            print_char((char)(*(char*)(str + i)));
            i++;
        }
        else{break;}
    }
}

#define va_list __builtin_va_list

#define va_rounded_size(type) \
(((sizeof(type) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))

#define va_start(v,l)   __builtin_va_start(v,l)
#define va_end(v)       __builtin_va_end(v)
#define va_arg(v,l)     __builtin_va_arg(v,l)

static void print_int(const uint32_t i)
{
    uint32_t n = 0;
    uint32_t d = 1000000000;
    
    while((i / d == 0) && (d >= 10))
    {d /= 10;}
  
    n = i;
  
    while(d >= 10)
    {
        print_char((char)('0' + n / d));
        n = n % d;
        d /= 10;
    }
    print_char((char)('0' + n));
}   

static void print_hex(const uint32_t i)
{
    const uint8_t hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
                              '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  
    uint32_t n = 0;
    uint32_t d = 0x10000000;
    
    while((i / d == 0) && (d >= 0x10))
    {d /= 0x10;}
    
    n = i;
    
    if(d < 0x10){print_char('0');}
    while(d >= 0xF)
    {
        print_char((char)(hex[n / d]));
        n = n % d;
        d /= 0x10;
    }

    print_char((char)(hex[n]));
}

uint8_t _x, _y;

void set_cursor_to_info_bar(void)
{
    _x = x;
    _y = y;
    
    x = 0;
    y = 24;
}

void restore_cursor_from_info_bar(void)
{
    x = _x;
    y = _y;
}

void printf(const char * const str, ...)
{
    int i, location;
    va_list ap;
    va_start(ap, str);
    
    i = 0;
    
    while(str[i])                       /* continue while there are still characters in the buffer */
    {
        if(str[i] == '%')               /* do we have a control character? */
        {   
            switch(str[++i])            /* figure out what kind of control character it is */
            {
                case 'c':               /* %c is a single character */
                    print_char((char)va_arg(ap, int));
                    break;
                    
                case 's':               /* %s is a character string */
                    print_string(va_arg(ap, char*));
                    break;
                
                case 'u':               /* %u/d is an unsigned integer */
                case 'd':
                    print_int(va_arg(ap, unsigned int));
                    break;
                    
                case 'x':               /* %x is an unsigned hex integer */
                    print_string("0x");
                case 'm':
                    print_hex(va_arg(ap, unsigned int));
                    break;    
                
                default:                /* if it is an unknown control, then just print the character and the '%' */
                    print_char(str[i-1]);
                    print_char(str[i]);
                    break;
            }
            
            i++;
        }
        else                            /* if it was just a normal character, print it */
        {
            print_char(str[i++]);
        }
    }
    
    va_end(ap);   
}







